package com.lixinwen.gdgame.game.util;

import com.lixinwen.gdgame.game.entities.Hand;
import com.lixinwen.gdgame.game.entities.enums.HandType;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class Picker {

    public static int[][] departByColorNum(int[] cards){
        int[][] ans = new int[4][14];
        for(int x:cards){
            ans[x/13][x%13]++;
        }
        for(int i=0;i<4;i++){
            ans[i][13] = ans[i][0];
        }
        return ans;
    }


    private static ArrayList<Integer>[] departByNum(int[] cards,Integer rank){
        ArrayList[] ans = new ArrayList[14];
        for(int i=0;i<14;i++){
            ans[i] = new ArrayList<Integer>();
        }
        for(int x:cards){
            if(x==12+rank)continue;
            ans[x%13].add(x);
        }
        ans[13] = (ArrayList) ans[0].clone();
        return ans;
    }

    public static int getHeartNum(int[] cards,Integer rank){
        int cnt = 0;
        for(int x:cards){
            if(x==12+rank)cnt++;
        }
        return cnt;
    }

    public static void pick(HandType type, Integer down,Integer high, HandType curType, ArrayList<Hand> ans,int[] cards,Integer rank){
        int low = 0;
        if(curType==type){
            low = down;
        }
        if(type==HandType.Single)doPickSingle(cards,low,high,rank,ans);
        else if(type==HandType.Pair)doPickPair(cards,low,high,rank,ans);
        else if(type==HandType.Trip)doPickTrip(cards,low,high,rank,ans);
        else if(type==HandType.Bomb4)doPickBomb4(cards,low,high,rank,ans);
        else if(type==HandType.Bomb5)doPickBomb5(cards,low,high,rank,ans);
        else if(type==HandType.Bomb6)doPickBomb6(cards,low,high,rank,ans);
        else if(type==HandType.Bomb7)doPickBomb7(cards,low,high,rank,ans);
        else if(type==HandType.Bomb8)doPickBomb8(cards,low,high,rank,ans);
        else if(type==HandType.Bomb9)doPickBomb9(cards,low,high,rank,ans);
        else if(type==HandType.Bomb10)doPickBomb10(cards,low,high,rank,ans);
        else if(type==HandType.JOKERBOMB)doPickJOKERBOMB(cards,low,high,rank,ans);
        else if(type==HandType.ThreePair)doPickThreePair(cards,low,high,rank,ans);
        else if(type==HandType.House)doPickHouse(cards,low,high,rank,ans);
        else if(type==HandType.Plate)doPickPlate(cards,low,high,rank,ans);
        else if(type==HandType.Straight)doPickStraight(cards,low,high,rank,ans);
        else if(type==HandType.StraightFlush)doPickStraightFlush(cards,low,high,rank,ans);
    }

    public static void doPickStraightFlush(int[] cards,Integer low,Integer high,Integer rank,ArrayList<Hand> ans) {
        int[][] colorTable;
        colorTable = departByColorNum(cards);
        int hearNum = colorTable[1][rank-1];
        colorTable[1][rank-1] = 0;
        for(int i=0;i<4;i++){
            for(int j=low;j<=10;j++){
                int k;
                int heartUse = 0;
                int[] tmp = new int[5];
                for(k=0;k<5;k++){
                    if(colorTable[i][j+k]==0){
                        if(heartUse==hearNum)break;
                        else {
                            heartUse++;
                            tmp[k] = 12 + rank;
                        }
                    }else{
                        if(k==4&&j==10)tmp[k]=13 * i + j + k-14;
                        else tmp[k] = 13 * i + j + k;

                    }
                }
                if(k==5){
                    ans.add(new Hand(HandType.StraightFlush.type, tmp,j));
                }
            }

        }
    }

    private static void straightDfs(ArrayList<Integer>[] table,ArrayList<Hand> ans,int index,int heartNum,ArrayList<Integer> tmp,Integer rank,int depth){
        if(depth==5){
            int[] t = new int[5];
            for(int i=0;i<5;i++){
                t[i] = tmp.get(i);
            }
            if(!Checker.isStraightFlush(t,rank))
                ans.add(new Hand(HandType.Straight.type, t, index-6));
            return;
        }
        for(int i=0;i<table[index].size();i++){
            tmp.add(table[index].get(i));
            straightDfs(table,ans,index+1,heartNum,tmp,rank,depth+1);
            tmp.remove(tmp.size()-1);
        }
        if(heartNum>0){
            tmp.add(12+rank);
            straightDfs(table,ans,index+1,heartNum-1,tmp,rank,depth+1);
            tmp.remove(tmp.size()-1);
        }

    }

    private static void doPickStraight(int[] cards,Integer low,Integer high,Integer rank,ArrayList<Hand> ans) {
        ArrayList<Integer>[] table = departByNum(cards,rank);
        int heartNum = getHeartNum(cards,rank);
        for(int i=low;i<10;i++){
            ArrayList<Integer> tmp = new ArrayList<>();
            straightDfs(table,ans,i,heartNum,tmp,rank,0);
        }

    }
    private static void C(ArrayList<Integer> slot,ArrayList<int[]> ans,int[] b,int m,int n){
        int i,j;
        for(i=n;i<=m;i++) {
            b[n-1] = i-1;
            if(n>1)
                C(slot,ans,b,i-1,n-1);
            else {
                int[] t = new int[b.length];
                for(j=0;j<b.length;j++){
                    t[j] = slot.get(b[j]);
                }
                ans.add(t);
            }
        }

    }
    public static ArrayList<int[]> selectFromOneSlot(ArrayList<Integer> slot,int num){
        ArrayList<int[]> arr = new ArrayList<>();
        int[] b = new int[slot.size()];
        C(slot,arr,b,slot.size(),num);
        return arr;
    }
    private static void plateDfs(ArrayList<Integer>[] table,ArrayList<Hand> ans,int index,int heartNum,ArrayList<Integer> tmp,Integer rank,Integer depth) {
        if(depth==2){
            int[] t = new int[6];
            for(int i=0;i<6;i++){
                t[i] = tmp.get(i);
            }
            ans.add(new Hand(HandType.Straight.type, t, index-2));
        }
        ArrayList<int[]> ChooseList = selectFromOneSlot(table[index],3);
        for(int[] t:ChooseList){
            for (int tt : t) { tmp.add(tt); }
            plateDfs(table,ans,index+1,heartNum,tmp,rank,depth+1);
        }
        if(heartNum>=1){
            ChooseList = selectFromOneSlot(table[index],2);
            for(int[] t:ChooseList){
                for (int tt : t) { tmp.add(tt); }
                tmp.add(rank+12);
                plateDfs(table,ans,index+1,heartNum-1,tmp,rank,depth+1);
            }
        }
        if(heartNum>=2){
            ChooseList = selectFromOneSlot(table[index],1);
            for(int[] t:ChooseList){
                for (int tt : t) { tmp.add(tt); }
                tmp.add(rank+12);
                tmp.add(rank+12);
                plateDfs(table,ans,index+1,heartNum-2,tmp,rank,depth+1);
            }
        }

    }
    private static void doPickPlate(int[] cards,Integer low,Integer high,Integer rank,ArrayList<Hand> ans) {
        ArrayList<Integer>[] table = departByNum(cards,rank);
        int heartNum = getHeartNum(cards,rank);
        for(int i=low;i<13;i++){
            ArrayList<Integer> tmp = new ArrayList<>();
            plateDfs(table,ans,i,heartNum,tmp,rank,0);
        }
    }

    private static void doPickHouse(int[] cards,Integer low,Integer high,Integer rank,ArrayList<Hand> ans) {
        ArrayList<Integer>[] table = departByNum(cards,rank);
        int heartNum = getHeartNum(cards,rank);
        for(int i=0;i<13;i++){
            for(int j=0;j<13;j++){
                if(i==j)continue;

            }
        }
    }

    private static void doPickThreePair(int[] cards,Integer low,Integer high,Integer rank,ArrayList<Hand> ans) {
    }

    private static void doPickJOKERBOMB(int[] cards,Integer low,Integer high,Integer rank,ArrayList<Hand> ans) {
    }

    private static void doPickBomb10(int[] cards,Integer low,Integer high,Integer rank,ArrayList<Hand> ans) {
    }

    private static void doPickBomb9(int[] cards,Integer low,Integer high,Integer rank,ArrayList<Hand> ans) {
    }

    private static void doPickBomb8(int[] cards,Integer low,Integer high,Integer rank,ArrayList<Hand> ans) {

    }

    private static void doPickBomb7(int[] cards,Integer low,Integer high,Integer rank,ArrayList<Hand> ans) {
    }

    private static void doPickBomb6(int[] cards,Integer low,Integer high,Integer rank,ArrayList<Hand> ans) {
    }

    private static void doPickBomb5(int[] cards,Integer low,Integer high,Integer rank,ArrayList<Hand> ans) {
    }

    private static void doPickBomb4(int[] cards,Integer low,Integer high,Integer rank,ArrayList<Hand> ans) {
    }

    private static void doPickTrip(int[] cards,Integer low,Integer high,Integer rank,ArrayList<Hand> ans) {

    }

    private static void doPickPair(int[] cards,Integer low,Integer high,Integer rank,ArrayList<Hand> ans) {

    }

    private static void doPickSingle(int[] cards,Integer low,Integer high,Integer rank,ArrayList<Hand> ans) {

    }

    public static void pickTribute(ArrayList<Integer> cards,ArrayList<Hand> ans,Integer rank) {
        ArrayList<Hand> tmp = new ArrayList<>();
        int max = 0;
        for(int card:cards){
            if(rank+12==card)continue;
            int r = getNormalRank(card,rank);
            if(r>max){
                max = r;
                tmp.clear();
                tmp.add(new Hand(HandType.Single.type, new int[]{card},r));
            }
            else if(r==max){
                tmp.add(new Hand(HandType.Single.type, new int[]{card},r));
            }
        }
        ans.addAll(tmp);
    }

    private static int getNormalRank(int card,Integer rank) {
        if(card==2000){
            return 17;
        }
        if(card==1000){
            return 16;
        }
        if(card%13==rank){
            return 15;
        }
        if(card%13==1){
            return 14;
        }
        return card%13;
    }

    public static void pickBack(ArrayList<Integer> cards, ArrayList<Hand> ans, Integer curRank) {
        for(int card:cards){
            if(card==2000)continue;
            if(card==1000)continue;
            int r = card%13;
            if(r>1&&r<=10&&r!=curRank){
                ans.add(new Hand(HandType.Single.type, new int[]{card},r));
            }
        }
    }
}
